﻿/******************************************************************************\
    Auxiliary.cpp contains plugin-depended functions.
    It's used with acm server plugins. See DllMain.cpp for details.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#include <winsock2.h>
#include <windows.h>
#include ".\Auxiliary.h"
#include <Common\String.h>
#include "..\Post.h"
#include <Common\Log.h>
#include <Common\KMP.h>
#include <Common\Random.h>
#include <Common\Memory.hpp>
#include <Common\Plugin.h>
#include <set>
using namespace std;

extern cLog Log;

cCriticalSection AboutCriticalSection;
const char Title[] = "Moodle system plugin " __DATE__;
const char Comment[] = "<a href='http://informatics.mccme.ru/moodle'>Moodle</a>";

#define KOI8R 20866

cCriticalSection LanguagesCriticalSection;
map< unsigned long, unsigned long > LanguageIndex;
vector< sLanguage > Language;

cCriticalSection ProblemsCriticalSection;
map< unsigned long, unsigned long > ProblemIndex;
vector< sProblem > Problem;

static const char Host[] = "informatics.mccme.ru";
static const string main_page = "http://informatics.mccme.ru/moodle/";
static const string login_page = main_page + "login/index.php";
static const string theme_page = main_page + "course/view.php?id=%lu";
static const string statements_page = main_page + "mod/statements/view3.php?id=%lu&standing";
static const string statement_page = main_page + "mod/statements/view3.php?id=%lu&chapterid=%lu";
//static const string statement_page = main_page + "mod/statements/view3.php?id=%lu&chapter_id=%lu";

cCriticalSection VariablesCriticalSection;
const unsigned long VariablesCount = 2;
const char *VariableProperties[ VariablesCount ] =
{
	"Name = Login;",
	"Name = Password;"
};
string VariableValue[ VariablesCount ];
static string &Login = VariableValue[ 0 ];
static string &Password = VariableValue[ 1 ];


cCriticalSection CompilationErrorCriticalSection;
std::map< unsigned long, std::string > CompilationError;

static cCriticalData< string > SessionID( "" );

bool LogIn()
{
	static cCriticalSection login_critical_section;
	cLocker locker( login_critical_section );
	try
	{
		static bool is_logged_in = false;
		if( is_logged_in )
			return true;

		string response, location;

		string login, password;
		{
			cLocker locker( VariablesCriticalSection );
			login = Login;
			password = Password;
		}

		if( !HttpPost( Host, login_page, StringPrintf( "username=%s&password=%s&testcookies=1", login.c_str(), password.c_str() ), response, location ) )
		{
			Log.Error( "Login::HttpPost returned false.\n" );
			return false;
		}
		if( location != main_page )
		{
			Log.Error( "Wrong login/password : location = %s\n", location.c_str());
			return false;
		}
		is_logged_in = true;
		return true;
	}
	catch( ... )
	{
		Log.Error( "Login: Exception O.o\n" );
		return false;
	}
}

bool findnext(const char *str, int &pos, int str_length, const cCIKMPString &open, const cCIKMPString &close, string& result)
{
	int op = open.FindIn(str+pos, str_length-pos);
	if (op<0||op>=str_length) return false;
	pos += op+open.Length();
	int len = close.FindIn(str+pos, str_length-pos);
	string res( str + pos, len);
	result = res;
	pos+=len+close.Length();
	return true;
}

bool LoadProblems()
{
	static const cCIKMPString theme_id_open = "<a  href=\"http://informatics.mccme.ru/moodle/course/view.php?id=", theme_id_close="\"",
			theme_name_open = ">", theme_name_close="</a>",
			statement_open="statements_toc_alpha", statement_close = "</div>",
			topic_open = "class=\"section main\">", topic_close = "class=\"right side\">",
			topic_name_open = "<strong>", topic_name_close = "</strong>",
			course_open = "mod/statements/view.php?id=", course_close = "\"",
			problem_name_open = "</B>", problem_name_close="<",
			problem_id_open = "chapterid=", problem_id_close="\"";
			
	cLocker locker( ProblemsCriticalSection );
	try
	{
		static bool is_problems_loaded = false;
 		if( is_problems_loaded )
			return true;

		if (!LogIn())
			return false;

		Problem.clear();
		ProblemIndex.clear();
		map<unsigned long, string> themes;
		map<unsigned long, string> courses;
		map<unsigned long, unsigned long> ct_index;
		map<unsigned long, string> problems;
		map<unsigned long, vector<unsigned long>> pc_index;
		string response;
		if( !HttpGet( Host, StringPrintf( main_page.c_str() ), response ) )
			{
				Log.Error( "LoadProblems::HttpGet returned false.\n" );
				return false;
			}

		const char *str = response.c_str();
		int pos = 0, len = ( int )response.length();
		string theme_id, theme_name;
		while (findnext(str, pos, len, theme_id_open, theme_id_close, theme_id) && 
			findnext(str, pos, len, theme_name_open, theme_name_close, theme_name))
		{
			themes[atoi(theme_id.c_str())] = theme_name;
			Log.Info("LoadProblems:: found theme %s\n", theme_name.c_str());
		}

		for (map<unsigned long, string>::const_iterator theme = themes.begin(); theme!=themes.end(); theme++)
		{
			//read courses
			if( !HttpGet( Host, StringPrintf( theme_page.c_str(), theme->first ), response ) )
			{
				Log.Error( "LoadProblems::HttpGet returned false.\n" );
				return false;
			}
			str = response.c_str();
			pos = 0; len = ( int )response.length();
			string topic_content;
			while (findnext(str, pos, len, topic_open, topic_close, topic_content))
			{
				const char *str2 = topic_content.c_str();
				int pos2 = 0, len2 = topic_content.length();
				string topic_name;
				if (!findnext(str2, pos2, len2, topic_name_open, topic_name_close, topic_name)) continue;
				string course_id;
				while (findnext(str2, pos2, len2, course_open, course_close, course_id))
				{
					unsigned int id = atoi(course_id.c_str());
					courses[id] = topic_name;
					ct_index[id] = theme->first;
					Log.Info("LoadProblems:: found topic %s\n", topic_name.c_str());
				}
			}
		}

		for (map<unsigned long, string>::const_iterator course = courses.begin(); course!=courses.end(); course++)
		{
			if( !HttpGet( Host, StringPrintf( statements_page.c_str(), course->first), response ) )
			{
				Log.Error( "LoadProblems::HttpGet returned false.\n" );
				return false;
			}
			str = response.c_str();
			pos = 0; len = ( int )response.length();
			string found;
			if (!findnext(str, pos, len, statement_open, statement_close, found))
				continue;
			pos = 0;
			str = found.c_str();
			len = found.length();
			string problem_name, problem_id;
			while (findnext(str, pos, len, problem_id_open, problem_id_close, problem_id) &&
				   findnext(str, pos, len, problem_name_open, problem_name_close, problem_name) )
			{
				unsigned long id = atoi(problem_id.c_str());
				if (problems.find(id)==problems.end())
				{
					problems[id]= problem_name;
					vector<unsigned long> temp;
					temp.push_back(course->first);
					pc_index[id]=temp;
				} else
					pc_index[id].push_back(course->first);
			}
		}

		for (map<unsigned long, string>::const_iterator problem = problems.begin(); problem!=problems.end(); problem++)
		{
			string name = problem->second;
			ProblemIndex.insert( make_pair( problem->first, Problem.size() ) );
			sProblem prob(problem->first, name );
			prob.ThemeId = pc_index[problem->first];
			Problem.push_back( prob );
			Log.Info("LoadProblems::found problem id=%lu %s\n", problem->first, name.c_str());
		}
		is_problems_loaded = true;
		return true;
	}
	catch( ... )
	{
		Log.Error( "LoadProblems: exception O.o\n" );
		return false;
	}
}

bool sProblem::Load()
{	
	cLocker locker( ProblemsCriticalSection );
	try
	{
		if( IsLoaded )
			return true;
		if( !LogIn() )
			return false;

		if( !HttpGet( Host, StringPrintf( statement_page.c_str(), ThemeId[0], Id ), Body ) )
		{
			Log.Error( "sProblem::Load::HttpGet returned false.\n" );
			return false;
		}

		if( !Cut( Body, "<div class=\"statements_content\">", "class='statements_chapter_title'" ) )
		{
			Log.Error( "sProblem::Load::Cut returned false.\n" );
			return false;
		}
		Cut(Body, "</div></div>", "<div id='submit'");

		string head = Header.substr(0, Header.find('<')); 
		string header = StringPrintf( "<html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"><title>%s</title><link rel=\"stylesheet\" href=\"/judge/judge.css\" type=\"text/css\"><link rel=\"shortcut icon\" href=\"/favicon.ico\"></head><body><div class=\"judge\"><div id=\"text\">", head.c_str() );
		header+="<h1>"+head+"</h1></br>";
		string tail = "</div></div></body></html>";
		Body = header + Body + tail;

		if( !LoadAdditions( Host, "/moodle/mod/statements/", Body, AdditionId ) )
		{
			Log.Error( "sProblem::Load::LoadAdditions returned false.\n" );
			return false;
		}
		IsLoaded = true;
		return true;
	}
	catch( ... )
	{
		Log.Error( "sProblem::Load: exception O.o\n" );
		return false;
	}
}

bool LoadLanguages()
{
	cLocker locker( LanguagesCriticalSection );
	try
	{
		static bool is_languages_loaded = false;
		if( is_languages_loaded )
			return true;
		if( !LogIn() )
			return false;

		string response;
		if( !HttpGet( Host, "http://informatics.mccme.ru/moodle/mod/statements/view3.php?id=192&chapterid=4", response ) )
		{
			Log.Error( "LoadLanguages::HttpGet returned false.\n" );
			return false;
		}
		if( !Cut( response, "<select name=\"lang_id\" id=\"lang_id\" onChange=\"alert(123);\">", "</select>" ) )
		{
			Log.Error( "LoadLanguages::Cut returned false.\n" );
			return false;
		}
		const char *str = response.c_str();
		int pos = 0, new_pos;
		int str_length = ( int )response.length();

		while( true )
		{
			static const cCIKMPString keystr( "<option value=\"" );
			new_pos = keystr.FindIn( str + pos, str_length - pos );
			if( new_pos == -1 )
				break;
			pos += new_pos + keystr.Length();

			unsigned long id;
			if( sscanf( str + pos, "%lu", &id ) != 1 )
				continue;

			new_pos = strchr( str + pos, '>' ) - ( str + pos );
			pos += new_pos + 1;
			new_pos = strchr( str + pos, '<' ) - ( str + pos );
			string name( str + pos, new_pos );
			pos += new_pos + 1;

			LanguageIndex.insert( make_pair( id, Language.size() ) );
			Language.push_back( sLanguage( id, name ) );
		}

		if( Language.size() == 0 )
		{
			Log.Error( "LoadLanguages: Condition (Language.size()==0) is true.\n" );
			return false;
		}

		is_languages_loaded = true;
		return true;
	}
	catch( ... )
	{
		Log.Error( "LoadLanguages: exception O.o\n" );
		return false;
	}
}

bool sProblem::GetLastSubmit( unsigned long long &sid )
{
	try
	{
		if( !LogIn() )
			return false;

		string response; 
		if( !HttpGet( Host, StringPrintf( "http://informatics.mccme.ru/moodle/mod/statements/view3.php?id=%lu&chapterid=%lu", ThemeId[ 0 ], Id ), response ) )
		{
			Log.Error( "GetLastSubmit::HttpGet returned false.\n" );
			return false;
		}
		int pos = 0, new_pos;
		const char *str = response.c_str();
		int str_length = ( int )response.length();

		const cCIKMPString sid_begin( "SID : '" );
		new_pos = sid_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + sid_begin.Length();
		if( sscanf( str + pos, "%I64x", &sid ) != 1 )
			return false;
		
		Log.Notice( "GetLastSubmit: sid = %016I64x\n", sid );

		const cCIKMPString prob_id_begin( "prob_id : '" );
		new_pos = prob_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + prob_id_begin.Length();
		if( sscanf( str + pos, "%lu", &virt_Id ) != 1 )
			return false;

		Log.Notice( "GetLastSubmit: virt_Id = %lu\n", virt_Id );

		const cCIKMPString problem_id_begin( "<div id=\"problem_id\" style=\"display: none\">" );
		new_pos = problem_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + problem_id_begin.Length();
		new_pos = strchr( str + pos, '<' ) - ( str + pos );
		string problem_id( str + pos, new_pos );

		const cCIKMPString group_id_begin( "<div id=\"group_id\" style=\"display: none\">" );
		new_pos = group_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + group_id_begin.Length();
		new_pos = strchr( str + pos, '<' ) - ( str + pos );
		string group_id( str + pos, new_pos );

		const cCIKMPString user_id_begin( "<div id=\"user_id\" style=\"display: none\">" );
		new_pos = user_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + user_id_begin.Length();
		new_pos = strchr( str + pos, '<' ) - ( str + pos );
		string user_id( str + pos, new_pos );

		const cCIKMPString language_id_begin( "<div id=\"language_id\" style=\"display: none\">" );
		new_pos = language_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + language_id_begin.Length();
		new_pos = strchr( str + pos, '<' ) - ( str + pos );
		string language_id( str + pos, new_pos );

		const cCIKMPString status_id_begin( "<div id=\"status_id\" style=\"display: none\">" );
		new_pos = status_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + status_id_begin.Length();
		new_pos = strchr( str + pos, '<' ) - ( str + pos );
		string status_id( str + pos, new_pos );

		const cCIKMPString statement_id_begin( "<div id=\"statement_id\" style=\"display: none\">" );
		new_pos = statement_id_begin.FindIn( str + pos, str_length - pos );
		if( new_pos == -1 )
			return false;
		pos += new_pos + statement_id_begin.Length();
		new_pos = strchr( str + pos, '<' ) - ( str + pos );
		string statement_id( str + pos, new_pos );

		if( !HttpGet( Host, StringPrintf( "http://informatics.mccme.ru/moodle/ajax/ajax.php?problem_id=%s&group_id=%s&user_id=%s&lang_id=%s&status_id=%s&statement_id=%s&objectName=submits&count=1&page=0&action=getHTMLTable", problem_id.c_str(), group_id.c_str(), user_id.c_str(), language_id.c_str(), status_id.c_str(), statement_id.c_str() ), response ) )
		{
			Log.Error( "GetLastSubmit::HttpGet returned false.\n" );
			return false;
		}

		pos = 0;
		str = response.c_str();
		str_length = ( int )response.length();

		const cCIKMPString trstr( "<tr>" ), tdstr( "<td>" );
		new_pos = trstr.FindIn( str + pos, str_length - pos );

		LastSubmitId = 0;
		do 
		{
			if( new_pos == -1 )
				break;
			pos += new_pos + trstr.Length();
			new_pos = tdstr.FindIn( str + pos, str_length - pos );
			if( new_pos == -1 )
				break;
			pos += new_pos + tdstr.Length();
			unsigned long submit_id;
			if( sscanf( str + pos, "%*u-%lu", &submit_id ) != 1 )
				break;
			LastSubmitId = submit_id;

			// Участник
			new_pos = tdstr.FindIn( str + pos, str_length - pos );
			pos += new_pos + tdstr.Length();
			new_pos = strchr( str + pos, '<' ) - ( str + pos );
			
			// Задача
			new_pos = tdstr.FindIn( str + pos, str_length - pos );
			pos += new_pos + tdstr.Length();
			new_pos = strchr( str + pos, '<' ) - ( str + pos );

			// Дата
			new_pos = tdstr.FindIn( str + pos, str_length - pos );
			pos += new_pos + tdstr.Length();
			new_pos = strchr( str + pos, '<' ) - ( str + pos );

			//Язык
			new_pos = tdstr.FindIn( str + pos, str_length - pos );
			pos += new_pos + tdstr.Length();
			new_pos = strchr( str + pos, '<' ) - ( str + pos );

			// Статус
			new_pos = tdstr.FindIn( str + pos, str_length - pos );
			pos += new_pos + tdstr.Length();
			new_pos = strchr( str + pos, '<' ) - ( str + pos );
			LastStatus = Trim( string( str + pos, new_pos ) );
		} while( false );

		Log.Notice( "GetLastSubmit: Last submit id = %lu; status = %s\n", LastSubmitId, LastStatus.c_str() );

		IsLastSubmitGotten = true;
		return true;
	}
	catch( ... )
	{
		Log.Error( "GetLastSubmit: Exception O.o\n" );
		return false;
	}
}

bool SubSubmit( unsigned long problem_index, unsigned long language_index, const string &source, long &result, unsigned short &test_number, unsigned long &time, unsigned long &memory )
{
	static cCriticalSection submit_critical_section;
	cLocker locker( submit_critical_section );
	try
	{
		if( !LogIn() )
			return false;

		string response, location;

		unsigned long problem_id, theme_id;
		unsigned long last_submit_id;
		unsigned long virt_id;
		unsigned long long sid;
		string last_status;
		bool dublicated_submit = false;
		{
			cLocker locker( ProblemsCriticalSection );
			problem_id = Problem[ problem_index ].Id;
			theme_id = Problem[ problem_index ].ThemeId.front();
			if( !Problem[ problem_index ].GetLastSubmit( sid ) )
				return false;
			virt_id =  Problem[ problem_index ].virt_Id;
			last_submit_id = Problem[ problem_index ].LastSubmitId;
			last_status = Problem[ problem_index ].LastStatus;
		}

		int language_id;
		{
			cLocker locker( LanguagesCriticalSection );
			language_id = Language[ language_index ].Id;
		}

		while( true )
		{
			string boundary = "---------------------------" + RandomString( 14 );
			if( !HttpMultiPost( Host, "http://informatics.mccme.ru/cgi-bin/new-client", boundary, StringPrintf( "--%s\r\nContent-Disposition: form-data; name=\"SID\"\r\n\r\n%016I64x\r\n--%s\r\nContent-Disposition: form-data; name=\"prob_id\"\r\n\r\n%lu\r\n--%s\r\nContent-Disposition: form-data; name=\"lang_id\"\r\n\r\n%lu\r\n--%s\r\nContent-Disposition: form-data; name=\"file\"; filename=\"filename.ext\"\r\nContent-Type: text/plain\r\n\r\n", boundary.c_str(), sid, boundary.c_str(), virt_id, boundary.c_str(), language_id, boundary.c_str() ) + source + StringPrintf( "\r\n--%s\r\nContent-Disposition: form-data; name=\"action_40\"\r\n\r\nОтправить!\r\n--%s--\r\n", boundary.c_str(), boundary.c_str() ), response, location ) )
			{
				Log.Error( "SubSubmit::HttpPost returned false.\n" );
				return false;
			}

			if( location == StringPrintf( "http://informatics.mccme.ru/cgi-bin/new-client?SID=%016I64x&action=139&prob_id=%lu", sid, virt_id ) )
				break;

			static const cCIKMPString dublicated = "Р­С‚Р° РїРѕСЃС‹Р»РєР° СЏРІР»СЏРµС‚СЃСЏ РєРѕРїРёРµР№ РїРѕСЃС‹Р»РєРё";
			if( dublicated.FindIn( response.c_str(), response.length() ) != -1 )
			{
				//dublicated result!
				dublicated_submit = true;
				break;
			}

			static const cCIKMPString source_limit = "РџСЂРµРІС‹С€РµРЅР° РєРІРѕС‚Р° РїРѕР»СЊР·РѕРІР°С‚РµР»СЏ. Р”Р°РЅРЅР°СЏ РїРѕСЃС‹Р»РєР° РёРјРµРµС‚ СЃР»РёС€РєРѕРј Р±РѕР»СЊС€РѕР№ СЂР°Р·РјРµСЂ, Р’Р°РјРё СЃРґРµР»Р°РЅРѕ СЃР»РёС€РєРѕРј РјРЅРѕРіРѕ РїРѕСЃС‹Р»РѕРє, РёР»Рё СЃСѓРјРјР°СЂРЅС‹Р№ СЂР°Р·РјРµСЂ Р’Р°С€РёС… РїРѕСЃС‹Р»РѕРє СЃР»РёС€РєРѕРј РІРµР»РёРє.";
			if( source_limit.FindIn( response.c_str(), response.length() ) != -1 )
			{
				time = 0;
				memory = 0;
				test_number = 0;
				result = RESULT_SOURCE_LIMIT_EXCEEDED;
				return true;
			}

			Log.Error( "SubSubmit: Submission was not successful. Wrong location: %s instead of http://informatics.mccme.ru/cgi-bin/new-client?SID=%016I64x&action=137&prob_id=%lu\n", location.c_str(), sid, virt_id );
			return false;
		}

		const int MAX_COUNT = 100;
		int count = 0;

		while( true )
		{
			unsigned long submit_id;
			string submission_result;
			{
				cLocker locker( ProblemsCriticalSection );
				if( !Problem[ problem_index ].GetLastSubmit( sid ) )
				{
					Log.Error( "SubSubmit: Unable to get last submit." );
					return false;
				}
				submit_id = Problem[ problem_index ].LastSubmitId;
				submission_result = Problem[ problem_index ].LastStatus;
			}

			if( submit_id == last_submit_id && !dublicated_submit )
			{
				++count;
				if( count == MAX_COUNT )
				{
					Log.Error( "SubSubmit: Too many loops.\n" );
					return false;
				}
				Sleep( 100 );
				continue;
			}

			time = 0;
			memory = 0;
			test_number = 0;

			if( submission_result == "\\u041a\\u043e\\u043c\\u043f\\u0438\\u043b\\u0438\\u0440\\u043e\\u0432\\u0430\\u043d\\u0438\\u0435...\\n" || 
				 submission_result == "\\u0422\\u0435\\u0441\\u0442\\u0438\\u0440\\u043e\\u0432\\u0430\\u043d\\u0438\\u0435...\\n" ||
				 submission_result == "\\n" )  
			{
				++count;
				if( count == MAX_COUNT * 100 )
				{
					Log.Error( "SubSubmit: Too many loops.\n" );
					return false;
				}
				Sleep( 100 );
				continue;
			}

			if( submission_result == "OK\\n" ||
				 submission_result == "\\u0427\\u0430\\u0441\\u0442\\u0438\\u0447\\u043d\\u043e\\u0435 \\u0440\\u0435\\u0448\\u0435\\u043d\\u0438\\u0435\\n" || // Частичное решение
				 submission_result == "\\u041d\\u0435\\u043f\\u0440\\u0430\\u0432\\u0438\\u043b\\u044c\\u043d\\u044b\\u0439 \\u043e\\u0442\\u0432\\u0435\\u0442\\n" || // Неправильный ответ
				 submission_result == "\\u041f\\u0440\\u0435\\u0432\\u044b\\u0448\\u0435\\u043d\\u043e \\u043c\\u0430\\u043a\\u0441\\u0438\\u043c\\u0430\\u043b\\u044c\\u043d\\u043e\\u0435 \\u0432\\u0440\\u0435\\u043c\\u044f \\u0440\\u0430\\u0431\\u043e\\u0442\\u044b\\n" || // Превышено максимальное время работы
				 submission_result == "\\u041e\\u0448\\u0438\\u0431\\u043a\\u0430 \\u0432\\u043e \\u0432\\u0440\\u0435\\u043c\\u044f \\u0432\\u044b\\u043f\\u043e\\u043b\\u043d\\u0435\\u043d\\u0438\\u044f \\u043f\\u0440\\u043e\\u0433\\u0440\\u0430\\u043c\\u043c\\u044b\\n" || // Ошибка во время выполнения программы
				 submission_result == "\\u041d\\u0435\\u043f\\u0440\\u0430\\u0432\\u0438\\u043b\\u044c\\u043d\\u044b\\u0439 \\u0444\\u043e\\u0440\\u043c\\u0430\\u0442 \\u0432\\u044b\\u0432\\u043e\\u0434\\u0430\\n" ) // Неправильный формат вывода 
			{
				if( !HttpGet( Host, StringPrintf( "http://informatics.mccme.ru/cgi-bin/new-client?SID=%016I64x&run_id=%lu&action=37&moodle", sid, submit_id ), response ) )
				{
					Log.Error( "SubSubmit: Unable to get result details." );
					return false;
				}

				int pos = 0, new_pos;
				const char *str = response.c_str();
				int str_length = ( int )response.length();

				if( submission_result == "OK\\n" )		
					result = RESULT_ACCEPTED;
				else
					result = RESULT_SUBMISSION_ERROR;

				while( true )
				{
					cCIKMPString td_str( "<td class=\"b1\">" );
					cCIKMPString std_str( "</td>" );
					cCIKMPString str_str( "</tr>" );

					// номер теста
					new_pos = td_str.FindIn( str + pos, str_length - pos );
					if( new_pos == -1 )
					{
						Log.Error( "SubSubmit: unable to find test number" );
						break;
					}
					pos += new_pos + td_str.Length();
					if( sscanf( str + pos, "%hu", &test_number ) != 1 )
					{
						Log.Error( "SubSubmit: unable to read test number" );
						break;
					}

					// результат
					new_pos = td_str.FindIn( str + pos, str_length - pos );
					if( new_pos == -1 )
					{
						Log.Error( "SubSubmit: unable to find result" );
						break;
					}
					pos += new_pos + td_str.Length();
					new_pos = std_str.FindIn( str + pos, str_length - pos );
					if( new_pos == -1 )
					{
						Log.Error( "SubSubmit: unable to locate result" );
						break;
					}
					string submission_result( str + pos, new_pos );
					pos += new_pos + std_str.Length();

					// время
					new_pos = td_str.FindIn( str + pos, str_length - pos );
					if( new_pos == -1 )
					{
						Log.Error( "SubSubmit: unable to find run time" );
						break;
					}
					pos += new_pos + td_str.Length();
					double run_time;
					if( strncmp( str + pos, "&gt;", 4 ) == 0 )
						pos += 4;
					if( sscanf( str + pos, "%lf", &run_time ) != 1 )
					{
						Log.Error( "SubSubmit: unable to read run time" );
						break;
					}
					unsigned long int_time = ( unsigned long )( run_time * 1000 + 0.5 );
					if( time < int_time )
						time = int_time;

					// пропускаем оставшиеся поля (баллы, если есть)
					new_pos = str_str.FindIn( str + pos, str_length - pos );
					if( new_pos == -1 )
					{
						Log.Error( "SubSubmit: unable to find end of row" );
						break;
					}
					pos += new_pos + str_str.Length();

					if( submission_result == "<font color=\"green\">OK</font>" )
						continue;
					else if( submission_result == "<font color=\"red\">РќРµРїСЂР°РІРёР»СЊРЅС‹Р№ РѕС‚РІРµС‚</font>" )
						result = RESULT_WRONG_ANSWER;
					else if( submission_result == "<font color=\"red\">РџСЂРµРІС‹С€РµРЅРѕ РјР°РєСЃРёРјР°Р»СЊРЅРѕРµ РІСЂРµРјСЏ СЂР°Р±РѕС‚С‹</font>" )
						result = RESULT_TIME_LIMIT_EXCEEDED;
					else if( submission_result == "<font color=\"red\">РћС€РёР±РєР° РІС‹РїРѕР»РЅРµРЅРёСЏ</font>" )
						result = RESULT_RUN_TIME_ERROR;
					else if( submission_result == "<font color=\"red\">РќРµРїСЂР°РІРёР»СЊРЅС‹Р№ С„РѕСЂРјР°С‚ РІС‹РІРѕРґР°</font>" )
						result = RESULT_PRESENTATION_ERROR;
					else
					{
						Log.Error( "SubSubmit: Unknown sub result \"%s\"\n", submission_result.c_str() );
						return false;
					}
					break;
				}
				if( result == RESULT_ACCEPTED )
					test_number = 0;
				else if( result == RESULT_SUBMISSION_ERROR )
					return false;
			}
			else if( submission_result == "\\u041e\\u0448\\u0438\\u0431\\u043a\\u0430 \\u043a\\u043e\\u043c\\u043f\\u0438\\u043b\\u044f\\u0446\\u0438\\u0438\\n" ) // Ошибка компиляции
			{
				if( !HttpGet( Host, StringPrintf( "http://informatics.mccme.ru/cgi-bin/new-client?SID=%016I64x&run_id=%lu&action=37&moodle", sid, submit_id ), response ) )
				{
					Log.Error( "SubSubmit: Unable to get compilation error details." );
					return false;
				}
				if( !Cut( response, "<pre>", "</pre>" ) )
				{
					Log.Error( "Unable to retrieve compilation error message." );
					return false;
				}
				result = RESULT_COMPILATION_ERROR;
				{
					cLocker locker( CompilationErrorCriticalSection );
					CompilationError[ GetCurrentThreadId() ] = response;
				}
			}
			else
			{			
				Log.Error( "SubSubmit: Unknown result \"%s\"\n", submission_result.c_str() );
				return false;
			}
			break;
		}         

		return true;
	}
	catch( ... )
	{
		Log.Error( "SubSubmit: exception O.o\n" );
		return false;
	}
}
